001 /*
002 * Copyright 2005 Stephen J. McConnell
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
013 * implied.
014 *
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package net.dpml.tools.tasks;
020
021 import java.io.File;
022 import java.util.ArrayList;
023 import java.util.List;
024
025 import net.dpml.lang.UnknownKeyException;
026
027 import net.dpml.library.Resource;
028 import net.dpml.library.ResourceNotFoundException;
029 import net.dpml.library.Type;
030 import net.dpml.library.info.Scope;
031
032 import net.dpml.transit.Artifact;
033 import net.dpml.transit.Transit;
034 import net.dpml.transit.Layout;
035
036 import org.apache.tools.ant.BuildException;
037 import org.apache.tools.ant.taskdefs.Copy;
038
039 /**
040 * Consolidates a set of resources based on project dependencies.
041 *
042 * @author <a href="http://www.dpml.net">The Digital Product Meta Library</a>
043 * @version 1.0.0
044 */
045 public class ReplicateTask extends GenericTask
046 {
047 private String m_key;
048 private String m_ref;
049 private File m_todir;
050 private String m_layout;
051
052 private boolean m_verbose = false;
053 private boolean m_self = false;
054
055 private ArrayList m_includes = new ArrayList();
056
057 /**
058 * Set the key of the target project or resource.
059 *
060 * @param key the resource key
061 */
062 public void setKey( final String key )
063 {
064 m_key = key;
065 }
066
067 /**
068 * Set the ref of the target project or resource.
069 *
070 * @param ref the resource reference
071 */
072 public void setRef( final String ref )
073 {
074 m_ref = ref;
075 }
076
077 /**
078 * Set the id of the target layout strategy.
079 *
080 * @param id the layout identifier
081 */
082 public void setLayout( final String id )
083 {
084 m_layout = id;
085 }
086
087 /**
088 * Set the verbose policy.
089 * @param flag the verbose flag
090 */
091 public void setVerbose( boolean flag )
092 {
093 m_verbose = flag;
094 }
095
096 /**
097 * Settting self to TRUE will result in expansion of the path to include
098 * the target resource.
099 * @param flag the self inclusion flag
100 */
101 public void setSelf( boolean flag )
102 {
103 m_self = flag;
104 }
105
106 /**
107 * Create and add a new include.
108 * @return the include
109 */
110 public Include createInclude()
111 {
112 Include include = new Include();
113 m_includes.add( include );
114 return include;
115 }
116
117 /**
118 * The target directory to copy cached based path elements to.
119 * @param todir the destination directory
120 */
121 public void setTodir( File todir )
122 {
123 m_todir = todir;
124 }
125
126 /**
127 * Execute the task.
128 */
129 public void execute()
130 {
131 if( null == m_todir )
132 {
133 File target = getContext().getTargetDirectory();
134 m_todir = new File( target, "replicate" );
135 }
136
137 File destination = m_todir;
138 Layout layout = resolveLayout();
139 ArrayList list = new ArrayList();
140 String ref = getRef();
141 if( null != ref )
142 {
143 Resource resource = getResource( ref );
144 aggregate( list, resource, m_self );
145 }
146
147 //
148 // add nested includes
149 //
150
151 Include[] includes = (Include[]) m_includes.toArray( new Include[0] );
152 for( int i=0; i<includes.length; i++ )
153 {
154 Include include = includes[i];
155 String includeRef = include.getRef();
156 Resource resource = getResource( includeRef );
157 aggregate( list, resource, true );
158 }
159
160 //
161 // get sorted list of resources
162 //
163
164 Resource[] resources = (Resource[]) list.toArray( new Resource[0] );
165 Resource[] selection = getContext().getLibrary().sort( resources );
166 File cache = (File) getProject().getReference( "dpml.cache" );
167 for( int i=0; i<selection.length; i++ )
168 {
169 Resource resource = selection[i];
170 copy( cache, destination, resource, layout );
171 }
172 }
173
174 /**
175 * Copy the artifacts produced by the Resource from the cache to
176 * the destination using a suppplied target layout.
177 */
178 private void copy( File cache, File destination, Resource resource, Layout layout )
179 {
180 Type[] types = resource.getTypes();
181 for( int j=0; j<types.length; j++ )
182 {
183 Type type = types[j];
184 String id = type.getID();
185
186 Artifact artifact = resource.getArtifact( id );
187 copyArtifact( artifact, cache, destination, layout );
188 if( null != type.getVersion() )
189 {
190 Artifact link = resource.getLinkArtifact( id );
191 copyArtifact( link, cache, destination, layout );
192 }
193 }
194 }
195
196 private void copyArtifact( Artifact artifact, File cache, File destination, Layout layout )
197 {
198 String sourcePath = Transit.getInstance().getCacheLayout().resolvePath( artifact );
199 File source = new File( cache, sourcePath );
200 if( !source.exists() )
201 {
202 final String error =
203 "Cached resource ["
204 + source
205 + "] does not exist.";
206 log( error );
207 }
208 else
209 {
210 String destPath = layout.resolvePath( artifact );
211 File dest = new File( destination, destPath );
212 copyFile( source, dest );
213 File md5 = new File( cache, sourcePath + ".md5" );
214 if( md5.exists() )
215 {
216 copyFile( md5, new File( destination, destPath + ".md5" ) );
217 }
218 File asc = new File( cache, sourcePath + ".asc" );
219 if( asc.exists() )
220 {
221 copyFile( asc, new File( destination, destPath + ".asc" ) );
222 }
223 }
224 }
225
226 private void copyFile( File source, File dest )
227 {
228 dest.getParentFile().mkdir();
229 final Copy copy = (Copy) getProject().createTask( "copy" );
230 copy.setFile( source );
231 copy.setTofile( dest );
232 copy.setPreserveLastModified( true );
233 copy.setVerbose( m_verbose );
234 copy.init();
235 copy.execute();
236 }
237
238 private Layout resolveLayout()
239 {
240 if( null == m_layout )
241 {
242 return Transit.getInstance().getCacheLayout();
243 }
244 else
245 {
246 try
247 {
248 return Transit.getInstance().getLayout( m_layout );
249 }
250 catch( UnknownKeyException e )
251 {
252 final String error =
253 "Target layout id ["
254 + m_layout
255 + "] is unknown.";
256 throw new BuildException( error, e, getLocation() );
257 }
258 catch( Exception e )
259 {
260 final String error =
261 "Unexpected error while resolving layout: " + m_layout;
262 throw new BuildException( error, e, getLocation() );
263 }
264 }
265 }
266
267 private void aggregate( List list, Resource resource, boolean self )
268 {
269 Resource[] resources = resource.getAggregatedProviders( Scope.RUNTIME, true, false );
270 for( int i=0; i<resources.length; i++ )
271 {
272 Resource r = resources[i];
273 if( !list.contains( r ) )
274 {
275 list.add( r );
276 }
277 }
278 if( self )
279 {
280 if( !list.contains( resource ) )
281 {
282 list.add( resource );
283 }
284 }
285 }
286
287 private Resource[] getPathResources( Resource resource )
288 {
289 Resource[] resources = resource.getAggregatedProviders( Scope.RUNTIME, true, false );
290 if( m_self )
291 {
292 Resource[] result = new Resource[ resources.length + 1 ];
293 System.arraycopy( resources, 0, result, 0, resources.length );
294 result[ resources.length ] = resource;
295 return result;
296 }
297 else
298 {
299 return resources;
300 }
301 }
302
303
304 private String getRef()
305 {
306 if( null != m_ref )
307 {
308 return m_ref;
309 }
310 else if( null != m_key )
311 {
312 return getResource().getParent().getResourcePath() + "/" + m_key;
313 }
314 else
315 {
316 return getResource().getResourcePath();
317 }
318 }
319
320 private Resource getResource( String ref )
321 {
322 try
323 {
324 return getContext().getLibrary().getResource( ref );
325 }
326 catch( ResourceNotFoundException e )
327 {
328 final String error =
329 "Feature reference ["
330 + ref
331 + "] in the project ["
332 + getResource()
333 + "] is unknown.";
334 throw new BuildException( error, e );
335 }
336 }
337
338 /**
339 * Declaration of an include.
340 */
341 public class Include
342 {
343 private String m_includekey;
344 private String m_includeRef;
345
346 /**
347 * Set the key of the target project or resource.
348 *
349 * @param key the resource key
350 */
351 public void setKey( final String key )
352 {
353 m_includekey = key;
354 }
355
356 /**
357 * Set the ref of the target project or resource.
358 *
359 * @param ref the resource reference
360 */
361 public void setRef( final String ref )
362 {
363 m_includeRef = ref;
364 }
365
366 private String getRef()
367 {
368 if( null != m_includeRef )
369 {
370 return m_includeRef;
371 }
372 else if( null != m_includekey )
373 {
374 return getResource().getParent().getResourcePath() + "/" + m_includekey;
375 }
376 else
377 {
378 final String error =
379 "Missing 'ref' or 'key' attribute.";
380 throw new BuildException( error, getLocation() );
381 }
382 }
383 }
384 }